<?php
/**
 * TinyCatPHP 极简、快速、创新、易用、高性能的PHP应用开发框架
 * User: CnTaiGe <hst042@163.com>
 * Date: 2017/10/29
 */

/**
 * 处理路由
 */

namespace TinyCatPHP\libs;

use TinyCatPHP\libs\config\Config;
use TinyCatPHP\libs\utils\File;

class Routes
{
    /**
     * 保存所有类文件
     * @var array
     */
    protected $dirFiles = [];
    /**
     * 全局路由
     * @var array
     */
    protected $routers = [];
    protected static $_instance = null;

    private function __construct()
    {
    }

    protected function init()
    {
        $files = $this->getFiles();
        foreach ($files as $fk) {
            $fkT = str_replace('\\', '/', $fk);
            $isMatch = preg_match('#/config/|/cache/|/common/|/logs/|/view/#', $fkT);
            if ($isMatch === 0) $this->create($fk);
        }
    }

    /**
     *
     * @return Routes
     */
    public static function instance()
    {
        if (Routes::$_instance === null) {
            Routes::$_instance = new self();
        }
        return Routes::$_instance;
    }

    /**取目录下所有php文件
     * @param $dir
     * @return array
     */
    protected function dirToArray($dir)
    {
        $cdir = scandir($dir);
        foreach ($cdir as $value) {
            if (!in_array($value, array(".", ".."))) {
                if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) {
                    self::dirToArray($dir . DIRECTORY_SEPARATOR . $value);
                } else {
                    $pathInfo = pathinfo($dir . DIRECTORY_SEPARATOR . $value);
                    if (isset($pathInfo['extension']) && $pathInfo['extension'] === 'php')
                        $this->dirFiles[] = $dir . DIRECTORY_SEPARATOR . $value;
                }
            }
        }
    }

    /**
     * 获取所有类文件
     * @return array
     */
    protected function getFiles()
    {
        $this->dirToArray(APP_PATH);
        return $this->dirFiles;
    }

    /**创建路由
     * @param string $file 要注册的类文件
     * @throws \Exception
     */
    protected function create($file)
    {
        if (!file_exists($file))
            return;
        include_once $file;
        $cls = str_replace('/', '\\', str_replace('.php', '', str_replace(APP_PATH, '', $file)));
        if (!class_exists($cls)) return;
        $cls = new \ReflectionClass($cls);
        $methods = $cls->getMethods();
        foreach ($methods as $vk) {
            $doc = $vk->getDocComment();
            if ($doc === false) continue;
            //解析注解
            $pregContents = [];
            preg_match_all('/@.*/', $doc, $pregContents);
            //记录注册路由
            $route = [];
            //记录额外信息
            $extraInfo = [];
            foreach ($pregContents[0] as $preg) {
                switch (true) {
                    case (strpos($preg, '@route=') !== false):
                        $route[] = $preg;
                        break;
                    default:
                        $extraInfo[] = trim($preg);
                        break;
                }
            }
            if (count($route) != 0) {
                //处理路由参数
                foreach ($route as $routeSub) {
                    $routeSubs = explode('=', $routeSub);
                    if (isset($routeSubs[1]) && !empty($routeSubs[1])) {
                        $routeKey = trim($routeSubs[1]);
                        if (empty($routeKey)) continue;
                        if (isset($this->routers[$routeKey])) {
                            throw  new \Exception("路由 {$routeKey} 重复定义");
                        }
                        $this->routers[$routeKey] = [
                            'class' => $cls->getName(),
                            'method' => $vk->getName(),
                            'extra_info' => $extraInfo
                        ];
                    }
                }
            }
        }
    }

    /**
     * 获取所有路由
     * @return array
     */
    public function getRoutes()
    {
        $saveFile = Config::get('data_path') . '/cache/routes.php';
        //生产环境下缓存路由
        if (!Config::get('debug') && file_exists($saveFile)) {
            return include $saveFile;
        }
        self::init();
        $routes = $this->routers;
        $data = var_export($routes, true);
        File::save($saveFile, '<?php return ' . $data . ';', false);
        return include $saveFile;
    }

}